//
// Created by Asukai on 2022/6/5.
//


#include "pthreadPool.h"

const int ADDNUM = 2;

//任务结构体
typedef struct Task {
    void (*function)(void *arg);

    void *arg;
} Task;

//线程池结构体
struct ThreadPool {
    //任务队列
    Task *taskQ;

    int queueCapacity;      //容量
    int queueSize;          //当前任务个数
    int queueFront;         //队头->取出任务
    int queueRear;          //队尾->添加数据

    pthread_t managerID;    //管理者线程ID
    pthread_t *threadIDs;   //工作线程ID

    int minNum;             //最小线程个数
    int maxNum;             //最大线程个数

    int busyNum;            //忙线程个数:忙线程数量会频繁变化,对忙线程数操作时加锁防止出现问题
    int liveNum;            //存活线程个数

    int exitNum;            //需要杀死的线程个数

    pthread_mutex_t mutexPool;  //锁整个线程池
    pthread_mutex_t mutexBusy;  //锁busyNum成员
    pthread_cond_t notFull;      //任务队列是否为满
    pthread_cond_t notEmpty;    //任务队列是否为空

    int shutdown;               //判断是否需要销毁,需要销毁为1,不需要销毁为0
};

ThreadPool *threadPoolCreate(int min, int max, int queueSize) {
    ThreadPool *pool = (ThreadPool *) malloc(sizeof(ThreadPool));
    //使用do while(0)循环,当内存分配失败时跳出循环,并释放内存
    do {
        if (pool == NULL) {
            printf("malloc ThreadPool fail...\n");
            break;
        }

        pool->threadIDs = (pthread_t *) malloc(sizeof(pthread_t) * max);
        if (pool->threadIDs == NULL) {
            printf("malloc threadIDs fail...\n");
            break;
        }
        memset(pool->threadIDs, 0, sizeof(pthread_t) * max);

        pool->minNum = min;
        pool->maxNum = max;
        pool->busyNum = 0;
        pool->liveNum = min;
        pool->exitNum = 0;

        if (pthread_mutex_init(&pool->mutexPool, NULL) != 0 ||
            pthread_mutex_init(&pool->mutexBusy, NULL) != 0 ||
            pthread_cond_init(&pool->notEmpty, NULL) != 0 ||
            pthread_cond_init(&pool->notFull, NULL) != 0) {
            printf("mutex init fail....\n");
            break;
        }

        pool->taskQ = (Task *) malloc(sizeof(Task) * queueSize);
        pool->queueCapacity = queueSize;
        pool->queueSize = 0;
        pool->queueFront = 0;
        pool->queueRear = 0;

        pool->shutdown = 0;

        pthread_create(&pool->managerID, NULL, manager, pool);
        for (int i = 0; i < min; ++i) {
            pthread_create(&pool->threadIDs[i], NULL, worker, pool);
        }

        return pool;
    } while (0);

    if (pool->threadIDs) free(pool->threadIDs);
    if (pool->taskQ) free(pool->taskQ);
    if (pool) free(pool);

    return NULL;
}

void *worker(void *arg) {
    ThreadPool *pool = (ThreadPool *) arg;

    while (1) {
        pthread_mutex_lock(&pool->mutexPool);                   //加锁

        while (pool->queueSize == 0 && !pool->shutdown) {       //防止虚假唤醒:虚假唤醒见README
            //阻塞工作线程
            pthread_cond_wait(&pool->notEmpty, &pool->mutexPool);
            if (pool->exitNum > 0) {
                pool->exitNum--;
                if (pool->liveNum > pool->minNum) {
                    pool->liveNum--;
                    pthread_mutex_unlock(&pool->mutexPool);
                    threadExit(&pool);
                }
            }
        }
        if (pool->shutdown) {                                   //判断线程池是否销毁
            pthread_mutex_unlock(&pool->mutexPool);             //先解锁再退出线程,防止死锁
            threadExit(&pool);
        }

        Task task;                                              //将任务队列中的任务取出
        task.function = pool->taskQ[pool->queueFront].function;
        task.arg = pool->taskQ[pool->queueFront].arg;

        pool->queueFront = (pool->queueFront + 1) % pool->queueCapacity;    //将头指针往前移动,形成循环队列
        pool->queueSize--;                                      //任务队列中任务数减一

        pthread_cond_signal(&pool->notFull);                    //唤醒生产者

        pthread_mutex_unlock(&pool->mutexPool);                 //解锁

        printf("thread %ld start working....\n", pthread_self());
        pthread_mutex_lock(&pool->mutexBusy);                   //忙队列加锁
        pool->busyNum++;                                        //忙队列加一
        pthread_mutex_unlock(&pool->mutexBusy);                 //忙队列解锁

        task.function(task.arg);                                //处理任务
        free(task.arg);                                         //释放内存
        task.arg = NULL;                                        //内存置空

        printf("thread %ld end working....\n", pthread_self());
        pthread_mutex_lock(&pool->mutexBusy);                   //忙队列加锁
        pool->busyNum--;                                        //忙队列减一
        pthread_mutex_unlock(&pool->mutexBusy);                 //忙队列解锁
    }
}

void *manager(void *arg) {
    //获取线程池当前状态
    ThreadPool *pool = (ThreadPool *) arg;
    while (!pool->shutdown) {
        pthread_mutex_lock(&pool->mutexPool);
        int queueSize = pool->queueSize;
        int liveNum = pool->liveNum;
        pthread_mutex_unlock(&pool->mutexPool);

        pthread_mutex_lock(&pool->mutexBusy);
        int busyNum = pool->busyNum;
        pthread_mutex_unlock(&pool->mutexBusy);

        //添加线程
        //添加规则:
        //        当 任务队列中的任务数 比 存活线程 多
        //        并且 存活线程 比 最大线程数 少 时
        //        添加线程
        if (queueSize > liveNum && liveNum < pool->maxNum) {
            int count = 0;
            pthread_mutex_lock(&pool->mutexPool);
            for (int i = 0; count < ADDNUM && i < pool->maxNum && pool->liveNum < pool->maxNum; ++i) {
                if (pool->threadIDs[i] == 0) {
                    pthread_create(&pool->threadIDs[i], NULL, worker, pool);
                    count++;
                    pool->liveNum++;
                }
            }
            pthread_mutex_unlock(&pool->mutexPool);
        }

        //销毁线程
        //销毁规则:
        //        当     忙线程数*2 < 存活线程数
        //        并且   存活线程 > 最小线程数 时
        //              销毁线程
        if (busyNum * 2 > liveNum && liveNum > pool->minNum) {
            pthread_mutex_lock(&pool->mutexPool);
            pool->exitNum = ADDNUM;
            pthread_mutex_unlock(&pool->mutexPool);
            for (int i = 0; i < ADDNUM; i++) {
                pthread_cond_signal(&pool->notEmpty);
            }
        }

        sleep(1);

    }
    return NULL;
}

void threadExit(ThreadPool *pool) {
    pthread_t tid = pthread_self();
    for (int i = 0; i < pool->maxNum; i++) {
        if (pool->threadIDs[i] == tid) {
            pool->threadIDs[i] = 0;
            printf("thread %ld was exited", tid);
            break;
        }
    }
    pthread_exit(NULL);
}

void threadPoolAdd(ThreadPool *pool, void (*func)(void *), void *arg) {

    pthread_mutex_lock(&pool->mutexPool);

    while (pool->queueSize == pool->queueCapacity && !pool->shutdown) {
        pthread_cond_wait(&pool->notFull, &pool->mutexPool);
    }

    if (pool->shutdown) {
        pthread_mutex_unlock(&pool->mutexPool);
        return;
    }

    pool->taskQ[pool->queueRear].function = func;                   //往任务队列中添加任务
    pool->taskQ[pool->queueRear].arg = arg;                         //往任务队列中添加任务

    pool->queueRear = (pool->queueRear + 1) % pool->queueCapacity;    //尾指针移动,形成环形队列
    pool->queueSize++;                                              //任务队列中任务数加一

    pthread_cond_signal(&pool->notEmpty);                           //唤醒消费者

    pthread_mutex_unlock(&pool->mutexPool);
}

int threadPoolBusyNum(ThreadPool *pool) {
    pthread_mutex_lock(&pool->mutexBusy);
    int busyNum = pool->busyNum;
    pthread_mutex_unlock(&pool->mutexBusy);
    return busyNum;
}

int threadPoolLiveNum(ThreadPool *pool) {
    pthread_mutex_lock(&pool->mutexPool);
    int liveNum = pool->liveNum;
    pthread_mutex_unlock(&pool->mutexPool);
    return liveNum;
}

int threadPoolDestroy(ThreadPool *pool) {
    if (pool == NULL) {
        return -1;
    }
    pool->shutdown = 1;
    pthread_join(pool->managerID, NULL);
    for (int i = 0; i < pool->liveNum; i++) {
        pthread_cond_signal(&pool->notEmpty);
    }

    if (pool->threadIDs) free(pool->threadIDs);
    if (pool->taskQ) free(pool->taskQ);
    pthread_mutex_destroy(&pool->mutexPool);
    pthread_mutex_destroy(&pool->mutexBusy);
    pthread_cond_destroy(&pool->notEmpty);
    pthread_cond_destroy(&pool->notFull);
    free(pool);

    pool = NULL;

    return 0;
}
